using System;
using System.Globalization;
using System.Text;
using NFreeMarker.Template.Utility;

namespace NFreeMarker.Core
{
    /**
     * An instruction that outputs the value of a numerical expression.
     * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
     */
    public sealed class NumericalOutput : TemplateElement { // NOTE: Was internal

        private readonly Expression _expression;
        private readonly bool _hasFormat;
        private readonly int _minFracDigits;
        private readonly int _maxFracDigits;
        private volatile FormatHolder _formatCache; // creating new NumberFormat is slow operation

        internal NumericalOutput(Expression expression) {
            _expression = expression;
            _hasFormat = false;
            _minFracDigits = 0;
            _maxFracDigits = 0;
        }

        internal NumericalOutput(Expression expression,
                        int minFracDigits,
                        int maxFracDigits) 
        {
            _expression = expression;
            _hasFormat = true;
            _minFracDigits = minFracDigits;
            _maxFracDigits = maxFracDigits;
        }

        internal override void Accept(Environment env)
        {
            Number num = EvaluationUtil.GetNumber(_expression, env);
            
            FormatHolder fmth = _formatCache;  // atomic sampling
            if (fmth == null || !fmth.Locale.Equals(env.GetLocale()))
            {
                lock (this)
                {
                    fmth = _formatCache;
                    if (fmth == null || !fmth.Locale.Equals(env.GetLocale()))
                    {
                        //NumberFormat fmt = NumberFormat.getNumberInstance(env.GetLocale());
                        //if (_hasFormat)
                        //{
                        //    fmt.SetMinimumFractionDigits(_minFracDigits);
                        //    fmt.SetMaximumFractionDigits(_maxFracDigits);
                        //} else {
                        //    fmt.SetMinimumFractionDigits(0);
                        //    fmt.SetMaximumFractionDigits(50);
                        //}
                        //fmt.SetGroupingUsed(false);
                        //_formatCache = new FormatHolder(fmt, env.GetLocale());
                        //fmth = _formatCache;
                        throw new NotImplementedException();
                    }
                }
            }
            // We must use Format even if hasFormat == false.
            // Some locales may use non-Arabic digits, thus replacing the
            // decimal separator in the result of toString() is not enough.
            //env.GetOut().Write(fmth.format.format(num));
            throw new NotImplementedException();
        }

        public override string GetCanonicalForm() {
            var buf = new StringBuilder("#{");
            buf.Append(_expression.GetCanonicalForm());
            if (_hasFormat) {
                buf.Append(" ; ");
                buf.Append("m");
                buf.Append(_minFracDigits);
                buf.Append("M");
                buf.Append(_maxFracDigits);
            }
            buf.Append("}");
            return buf.ToString();
        }

        public override string GetDescription() {
            return GetSource();
        }

        internal override bool HeedsOpeningWhitespace() {
            return true;
        }

        internal override bool HeedsTrailingWhitespace() {
            return true;
        }

        private class FormatHolder
        {
        //    final NumberFormat format;
            internal readonly CultureInfo Locale;

            internal FormatHolder(/*NumberFormat format,*/ CultureInfo locale)
            {
                //this.format = format;
                Locale = locale;
            }
        }
    }
}